<?php

/**
 * JPGraph v4.0.3
 */

namespace lemon\JpGraph\Plot;

use lemon\JpGraph\Graph;
use lemon\JpGraph\Image;
use lemon\JpGraph\Util;

/*
 * File:		JPGRAPH_BAR.PHP
 * // Description: Bar plot extension for JpGraph
 * // Created:	 2001-01-08
 * // Ver:		 $Id: jpgraph_bar.php 1905 2009-10-06 18:00:21Z ljp $
 * //
 * // Copyright (c) Asial Corporation. All rights reserved.
 */
// Pattern for Bars
define('PATTERN_DIAG1', 1);
define('PATTERN_DIAG2', 2);
define('PATTERN_DIAG3', 3);
define('PATTERN_DIAG4', 4);
define('PATTERN_CROSS1', 5);
define('PATTERN_CROSS2', 6);
define('PATTERN_CROSS3', 7);
define('PATTERN_CROSS4', 8);
define('PATTERN_STRIPE1', 9);
define('PATTERN_STRIPE2', 10);

/**
 * @class BarPlot
 * // Description: Main code to produce a bar plot
 */
class BarPlot extends Plot
{
	public $fill				= false;
	public $fill_color		  = 'lightblue'; // Default is to fill with light blue
	public $iPattern			= -1;
	public $iPatternDensity	 = 80;
	public $iPatternColor	   = 'black';
	public $valuepos			= 'top';
	public $grad				= false;
	public $grad_style		  = 1;
	public $grad_fromcolor	  = [50, 50, 200];
	public $grad_tocolor		= [255, 255, 255];
	public $ymin				= 0;
	protected $width			= 0.4; // in percent of major ticks
	protected $abswidth		 = -1; // Width in absolute pixels
	protected $ybase			= 0; // Bars start at 0
	protected $align			= 'center';
	protected $bar_shadow	   = false;
	protected $bar_shadow_color = 'black';
	protected $bar_shadow_hsize = 3;
	protected $bar_shadow_vsize = 3;
	protected $bar_3d		   = false;
	protected $bar_3d_hsize	 = 3;
	protected $bar_3d_vsize	 = 3;

	/**
	 * CONSTRUCTOR.
	 *
	 * @param mixed $datay
	 * @param mixed $datax
	 */
	public function __construct($datay, $datax = false)
	{
		parent::__construct($datay, $datax);
		++$this->numpoints;
	}

	/**
	 * PUBLIC METHODS.
	 *
	 * @param mixed $aColor
	 * @param mixed $aHSize
	 * @param mixed $aVSize
	 * @param mixed $aShow
	 */
	// Set a drop shadow for the bar (or rather an "up-right" shadow)
	public function SetShadow($aColor = 'black', $aHSize = 3, $aVSize = 3, $aShow = true)
	{
		$this->bar_shadow	   = $aShow;
		$this->bar_shadow_color = $aColor;
		$this->bar_shadow_vsize = $aVSize;
		$this->bar_shadow_hsize = $aHSize;

		// Adjust the value margin to compensate for shadow
		$this->value->margin += $aVSize;
	}

	public function Set3D($aHSize = 3, $aVSize = 3, $aShow = true)
	{
		$this->bar_3d	   = $aShow;
		$this->bar_3d_vsize = $aVSize;
		$this->bar_3d_hsize = $aHSize;

		$this->value->margin += $aVSize;
	}

	// DEPRECATED use SetYBase instead
	public function SetYMin($aYStartValue)
	{
		//die("JpGraph Error: Deprecated function SetYMin. Use SetYBase() instead.");
		$this->ybase = $aYStartValue;
	}

	// Specify the base value for the bars
	public function SetYBase($aYStartValue)
	{
		$this->ybase = $aYStartValue;
	}

	// The method will take the specified pattern anre
	// return a pattern index that corresponds to the original
	// patterm being rotated 90 degreees. This is needed when plottin
	// Horizontal bars
	public function RotatePattern($aPat, $aRotate = true)
	{
		$rotate = [1 => 2, 2 => 1, 3 => 3, 4 => 5, 5 => 4, 6 => 6, 7 => 7, 8 => 8];
		if ($aRotate) {
			return $rotate[$aPat];
		}

		return $aPat;
	}

	public function Legend($graph)
	{
		if ($this->grad && $this->legend != '' && !$this->fill) {
			$color = [$this->grad_fromcolor, $this->grad_tocolor];
			// In order to differentiate between gradients and cooors specified as an Image\RGB triple
			$graph->legend->Add(
				$this->legend,
				$color,
				'',
				-$this->grad_style,
				$this->legendcsimtarget,
				$this->legendcsimalt,
				$this->legendcsimwintarget
			);
		} elseif ($this->legend != '' && ($this->iPattern > -1 || is_array($this->iPattern))) {
			if (is_array($this->iPattern)) {
				$p1 = $this->RotatePattern($this->iPattern[0], $graph->img->a == 90);
				$p2 = $this->iPatternColor[0];
				$p3 = $this->iPatternDensity[0];
			} else {
				$p1 = $this->RotatePattern($this->iPattern, $graph->img->a == 90);
				$p2 = $this->iPatternColor;
				$p3 = $this->iPatternDensity;
			}
			if ($p3 < 90) {
				$p3 += 5;
			}

			$color = [$p1, $p2, $p3, $this->fill_color];
			// A kludge: Too mark that we add a pattern we use a type value of < 100
			$graph->legend->Add(
				$this->legend,
				$color,
				'',
				-101,
				$this->legendcsimtarget,
				$this->legendcsimalt,
				$this->legendcsimwintarget
			);
		} elseif ($this->fill_color && $this->legend != '') {
			if (is_array($this->fill_color)) {
				$graph->legend->Add(
					$this->legend,
					$this->fill_color[0],
					'',
					0,
					$this->legendcsimtarget,
					$this->legendcsimalt,
					$this->legendcsimwintarget
				);
			} else {
				$graph->legend->Add(
					$this->legend,
					$this->fill_color,
					'',
					0,
					$this->legendcsimtarget,
					$this->legendcsimalt,
					$this->legendcsimwintarget
				);
			}
		}
	}

	// Gets called before any axis are stroked
	public function PreStrokeAdjust($graph)
	{
		parent::PreStrokeAdjust($graph);

		// If we are using a log Y-scale we want the base to be at the
		// minimum Y-value unless the user have specifically set some other
		// value than the default.
		if (substr($graph->axtype, -3, 3) == 'log' && $this->ybase == 0) {
			$this->ybase = $graph->yaxis->scale->GetMinVal();
		}

		// For a "text" X-axis scale we will adjust the
		// display of the bars a little bit.
		if (substr($graph->axtype, 0, 3) == 'tex') {
			// Position the ticks between the bars
			$graph->xaxis->scale->ticks->SetXLabelOffset(0.5, 0);

			// Center the bars
			if ($this->abswidth > -1) {
				$graph->SetTextScaleAbsCenterOff($this->abswidth);
			} else {
				if ($this->align == 'center') {
					$graph->SetTextScaleOff(0.5 - $this->width / 2);
				} elseif ($this->align == 'right') {
					$graph->SetTextScaleOff(1 - $this->width);
				}
			}
		} elseif (($this instanceof AccBarPlot) || ($this instanceof GroupBarPlot)) {
			// We only set an absolute width for linear and int scale
			// for text scale the width will be set to a fraction of
			// the majstep width.
			if ($this->abswidth == -1) {
				// Not set
				// set width to a visuable sensible default
				$this->abswidth = $graph->img->plotwidth / (2 * $this->numpoints);
			}
		}
	}

	public function Min()
	{
		$m = parent::Min();
		if ($m[1] >= $this->ybase) {
			$m[1] = $this->ybase;
		}

		return $m;
	}

	public function Max()
	{
		$m = parent::Max();
		if ($m[1] <= $this->ybase) {
			$m[1] = $this->ybase;
		}

		return $m;
	}

	// Specify width as fractions of the major stepo size
	public function SetWidth($aWidth)
	{
		if ($aWidth > 1) {
			// Interpret this as absolute width
			$this->abswidth = $aWidth;
		} else {
			$this->width = $aWidth;
		}
	}

	// Specify width in absolute pixels. If specified this
	// overrides SetWidth()
	public function SetAbsWidth($aWidth)
	{
		$this->abswidth = $aWidth;
	}

	public function SetAlign($aAlign)
	{
		$this->align = $aAlign;
	}

	public function SetNoFill()
	{
		$this->grad	   = false;
		$this->fill_color = false;
		$this->fill	   = false;
	}

	public function SetFillColor($aColor)
	{
		// Do an extra error check if the color is specified as an Image\RGB array triple
		// In that case convert it to a hex string since it will otherwise be
		// interpretated as an array of colors for each individual bar.

		$aColor		   = Image\RGB::tryHexConversion($aColor);
		$this->fill	   = true;
		$this->fill_color = $aColor;
	}

	public function SetFillGradient($aFromColor, $aToColor = null, $aStyle = null)
	{
		$this->grad		   = true;
		$this->grad_fromcolor = $aFromColor;
		$this->grad_tocolor   = $aToColor;
		$this->grad_style	 = $aStyle;
	}

	public function SetValuePos($aPos)
	{
		$this->valuepos = $aPos;
	}

	public function SetPattern($aPattern, $aColor = 'black')
	{
		if (is_array($aPattern)) {
			$n					 = safe_count($aPattern);
			$this->iPattern		= [];
			$this->iPatternDensity = [];
			if (is_array($aColor)) {
				$this->iPatternColor = [];
				if (safe_count($aColor) != $n) {
					Util\JpGraphError::RaiseL(2001); //('NUmber of colors is not the same as the number of patterns in BarPlot::SetPattern()');
				}
			} else {
				$this->iPatternColor = $aColor;
			}
			for ($i = 0; $i < $n; ++$i) {
				$this->_SetPatternHelper($aPattern[$i], $this->iPattern[$i], $this->iPatternDensity[$i]);
				if (is_array($aColor)) {
					$this->iPatternColor[$i] = $aColor[$i];
				}
			}
		} else {
			$this->_SetPatternHelper($aPattern, $this->iPattern, $this->iPatternDensity);
			$this->iPatternColor = $aColor;
		}
	}

	public function _SetPatternHelper($aPattern, &$aPatternValue, &$aDensity)
	{
		switch ($aPattern) {
			case PATTERN_DIAG1:
				$aPatternValue = 1;
				$aDensity	  = 92;

				break;
			case PATTERN_DIAG2:
				$aPatternValue = 1;
				$aDensity	  = 78;

				break;
			case PATTERN_DIAG3:
				$aPatternValue = 2;
				$aDensity	  = 92;

				break;
			case PATTERN_DIAG4:
				$aPatternValue = 2;
				$aDensity	  = 78;

				break;
			case PATTERN_CROSS1:
				$aPatternValue = 8;
				$aDensity	  = 90;

				break;
			case PATTERN_CROSS2:
				$aPatternValue = 8;
				$aDensity	  = 78;

				break;
			case PATTERN_CROSS3:
				$aPatternValue = 8;
				$aDensity	  = 65;

				break;
			case PATTERN_CROSS4:
				$aPatternValue = 7;
				$aDensity	  = 90;

				break;
			case PATTERN_STRIPE1:
				$aPatternValue = 5;
				$aDensity	  = 94;

				break;
			case PATTERN_STRIPE2:
				$aPatternValue = 5;
				$aDensity	  = 85;

				break;
			default:
				Util\JpGraphError::RaiseL(2002);
				//('Unknown pattern specified in call to BarPlot::SetPattern()');
		}
	}

	public function Stroke($img, $xscale, $yscale)
	{
		$numpoints = safe_count($this->coords[0]);
		if (isset($this->coords[1])) {
			if (safe_count($this->coords[1]) != $numpoints) {
				Util\JpGraphError::RaiseL(2003, safe_count($this->coords[1]), $numpoints);
			//"Number of X and Y points are not equal. Number of X-points:". safe_count($this->coords[1])."Number of Y-points:$numpoints");
			} else {
				$exist_x = true;
			}
		} else {
			$exist_x = false;
		}

		$numbars = safe_count($this->coords[0]);

		// Use GetMinVal() instead of scale[0] directly since in the case
		// of log scale we get a correct value. Log scales will have negative
		// values for values < 1 while still not representing negative numbers.
		if ($yscale->GetMinVal() >= 0) {
			$zp = $yscale->scale_abs[0];
		} else {
			$zp = $yscale->Translate(0);
		}

		if ($this->abswidth > -1) {
			$abswidth = $this->abswidth;
		} else {
			$abswidth = round($this->width * $xscale->scale_factor, 0);
		}

		// Count pontetial pattern array to avoid doing the count for each iteration
		if (is_array($this->iPattern)) {
			$np = safe_count($this->iPattern);
		}

		$grad = null;
		for ($i = 0; $i < $numbars; ++$i) {
			// If value is NULL, or 0 then don't draw a bar at all
			if ($this->coords[0][$i] === null || $this->coords[0][$i] === '') {
				continue;
			}

			if ($exist_x) {
				$x = $this->coords[1][$i];
			} else {
				$x = $i;
			}

			$x = $xscale->Translate($x);

			// Comment Note: This confuses the positioning when using acc together with
			// grouped bars. Workaround for fixing #191
			/*
			if( !$xscale->textscale ) {
			if($this->align=="center")
			$x -= $abswidth/2;
			elseif($this->align=="right")
			$x -= $abswidth;
			}
			 */
			// Stroke fill color and fill gradient
			$pts = [
				$x, $zp,
				$x, $yscale->Translate($this->coords[0][$i]),
				$x + $abswidth, $yscale->Translate($this->coords[0][$i]),
				$x + $abswidth, $zp, ];
			if ($this->grad) {
				if ($grad === null) {
					$grad = new Gradient($img);
				}
				if (is_array($this->grad_fromcolor)) {
					// The first argument (grad_fromcolor) can be either an array or a single color. If it is an array
					// then we have two choices. It can either a) be a single color specified as an Image\RGB triple or it can be
					// an array to specify both (from, to style) for each individual bar. The way to know the difference is
					// to investgate the first element. If this element is an integer [0,255] then we assume it is an Image\RGB
					// triple.
					$ng = safe_count($this->grad_fromcolor);
					if ($ng === 3) {
						if (is_numeric($this->grad_fromcolor[0]) && $this->grad_fromcolor[0] > 0 && $this->grad_fromcolor[0] < 256) {
							// Image\RGB Triple
							$fromcolor = $this->grad_fromcolor;
							$tocolor   = $this->grad_tocolor;
							$style	 = $this->grad_style;
						} else {
							$fromcolor = $this->grad_fromcolor[$i % $ng][0];
							$tocolor   = $this->grad_fromcolor[$i % $ng][1];
							$style	 = $this->grad_fromcolor[$i % $ng][2];
						}
					} else {
						$fromcolor = $this->grad_fromcolor[$i % $ng][0];
						$tocolor   = $this->grad_fromcolor[$i % $ng][1];
						$style	 = $this->grad_fromcolor[$i % $ng][2];
					}
					$grad->FilledRectangle(
						$pts[2],
						$pts[3],
						$pts[6],
						$pts[7],
						$fromcolor,
						$tocolor,
						$style
					);
				} else {
					$grad->FilledRectangle(
						$pts[2],
						$pts[3],
						$pts[6],
						$pts[7],
						$this->grad_fromcolor,
						$this->grad_tocolor,
						$this->grad_style
					);
				}
			} elseif (!empty($this->fill_color)) {
				if (is_array($this->fill_color)) {
					$img->PushColor($this->fill_color[$i % safe_count($this->fill_color)]);
				} else {
					$img->PushColor($this->fill_color);
				}
				$img->FilledPolygon($pts);
				$img->PopColor();
			}

			/////////////////////////kokorahen rectangle polygon//////////////////////

			// Remember value of this bar
			$val = $this->coords[0][$i];

			if (!empty($val) && !is_numeric($val)) {
				Util\JpGraphError::RaiseL(2004, $i, $val);
				//'All values for a barplot must be numeric. You have specified value['.$i.'] == \''.$val.'\'');
			}

			// Determine the shadow
			if ($this->bar_shadow && $val != 0) {
				$ssh = $this->bar_shadow_hsize;
				$ssv = $this->bar_shadow_vsize;
				// Create points to create a "upper-right" shadow
				if ($val > 0) {
					$sp[0]  = $pts[6];
					$sp[1]  = $pts[7];
					$sp[2]  = $pts[4];
					$sp[3]  = $pts[5];
					$sp[4]  = $pts[2];
					$sp[5]  = $pts[3];
					$sp[6]  = $pts[2] + $ssh;
					$sp[7]  = $pts[3] - $ssv;
					$sp[8]  = $pts[4] + $ssh;
					$sp[9]  = $pts[5] - $ssv;
					$sp[10] = $pts[6] + $ssh;
					$sp[11] = $pts[7] - $ssv;
				} elseif ($val < 0) {
					$sp[0]  = $pts[4];
					$sp[1]  = $pts[5];
					$sp[2]  = $pts[6];
					$sp[3]  = $pts[7];
					$sp[4]  = $pts[0];
					$sp[5]  = $pts[1];
					$sp[6]  = $pts[0] + $ssh;
					$sp[7]  = $pts[1] - $ssv;
					$sp[8]  = $pts[6] + $ssh;
					$sp[9]  = $pts[7] - $ssv;
					$sp[10] = $pts[4] + $ssh;
					$sp[11] = $pts[5] - $ssv;
				}
				if (is_array($this->bar_shadow_color)) {
					$numcolors = safe_count($this->bar_shadow_color);
					if ($numcolors == 0) {
						Util\JpGraphError::RaiseL(2005); //('You have specified an empty array for shadow colors in the bar plot.');
					}
					$img->PushColor($this->bar_shadow_color[$i % $numcolors]);
				} else {
					$img->PushColor($this->bar_shadow_color);
				}
				$img->FilledPolygon($sp);
				$img->PopColor();
			} elseif ($this->bar_3d && $val != 0) {
				// Determine the 3D

				$ssh = $this->bar_3d_hsize;
				$ssv = $this->bar_3d_vsize;

				// Create points to create a "upper-right" shadow
				if ($val > 0) {
					$sp1[0] = $pts[6];
					$sp1[1] = $pts[7];
					$sp1[2] = $pts[4];
					$sp1[3] = $pts[5];
					$sp1[4] = $pts[4] + $ssh;
					$sp1[5] = $pts[5] - $ssv;
					$sp1[6] = $pts[6] + $ssh;
					$sp1[7] = $pts[7] - $ssv;

					$sp2[0] = $pts[4];
					$sp2[1] = $pts[5];
					$sp2[2] = $pts[2];
					$sp2[3] = $pts[3];
					$sp2[4] = $pts[2] + $ssh;
					$sp2[5] = $pts[3] - $ssv;
					$sp2[6] = $pts[4] + $ssh;
					$sp2[7] = $pts[5] - $ssv;
				} elseif ($val < 0) {
					$sp1[0] = $pts[4];
					$sp1[1] = $pts[5];
					$sp1[2] = $pts[6];
					$sp1[3] = $pts[7];
					$sp1[4] = $pts[6] + $ssh;
					$sp1[5] = $pts[7] - $ssv;
					$sp1[6] = $pts[4] + $ssh;
					$sp1[7] = $pts[5] - $ssv;

					$sp2[0] = $pts[6];
					$sp2[1] = $pts[7];
					$sp2[2] = $pts[0];
					$sp2[3] = $pts[1];
					$sp2[4] = $pts[0] + $ssh;
					$sp2[5] = $pts[1] - $ssv;
					$sp2[6] = $pts[6] + $ssh;
					$sp2[7] = $pts[7] - $ssv;
				}

				$base_color = $this->fill_color;

				$img->PushColor($base_color . ':0.7');
				$img->FilledPolygon($sp1);
				$img->PopColor();

				$img->PushColor($base_color . ':1.1');
				$img->FilledPolygon($sp2);
				$img->PopColor();
			}

			// Stroke the pattern
			if (is_array($this->iPattern)) {
				$f = new Graph\RectPatternFactory();
				if (is_array($this->iPatternColor)) {
					$pcolor = $this->iPatternColor[$i % $np];
				} else {
					$pcolor = $this->iPatternColor;
				}
				$prect = $f->Create($this->iPattern[$i % $np], $pcolor, 1);
				$prect->SetDensity($this->iPatternDensity[$i % $np]);

				if ($val < 0) {
					$rx = $pts[0];
					$ry = $pts[1];
				} else {
					$rx = $pts[2];
					$ry = $pts[3];
				}
				$width  = abs($pts[4] - $pts[0]) + 1;
				$height = abs($pts[1] - $pts[3]) + 1;
				$prect->SetPos(new Util\Rectangle($rx, $ry, $width, $height));
				$prect->Stroke($img);
			} else {
				if ($this->iPattern > -1) {
					$f	 = new Graph\RectPatternFactory();
					$prect = $f->Create($this->iPattern, $this->iPatternColor, 1);
					$prect->SetDensity($this->iPatternDensity);
					if ($val < 0) {
						$rx = $pts[0];
						$ry = $pts[1];
					} else {
						$rx = $pts[2];
						$ry = $pts[3];
					}
					$width  = abs($pts[4] - $pts[0]) + 1;
					$height = abs($pts[1] - $pts[3]) + 1;
					$prect->SetPos(new Util\Rectangle($rx, $ry, $width, $height));
					$prect->Stroke($img);
				}
			}

			// Stroke the outline of the bar
			if (is_array($this->color)) {
				$img->SetColor($this->color[$i % safe_count($this->color)]);
			} else {
				$img->SetColor($this->color);
			}

			$pts[] = $pts[0];
			$pts[] = $pts[1];

			if ($this->weight > 0) {
				$img->SetLineWeight($this->weight);
				$img->Polygon($pts);
			}

			// Determine how to best position the values of the individual bars
			$x = $pts[2] + ($pts[4] - $pts[2]) / 2;
			$this->value->SetMargin(5);

			if ($this->valuepos == 'top') {
				$y = $pts[3];
				if ($img->a === 90) {
					if ($val < 0) {
						$this->value->SetAlign('right', 'center');
					} else {
						$this->value->SetAlign('left', 'center');
					}
				} else {
					if ($val < 0) {
						$this->value->SetMargin(-5);
						$y = $pts[1];
						$this->value->SetAlign('center', 'bottom');
					} else {
						$this->value->SetAlign('center', 'bottom');
					}
				}
				$this->value->Stroke($img, $val, $x, $y);
			} elseif ($this->valuepos == 'max') {
				$y = $pts[3];
				if ($img->a === 90) {
					if ($val < 0) {
						$this->value->SetAlign('left', 'center');
					} else {
						$this->value->SetAlign('right', 'center');
					}
				} else {
					if ($val < 0) {
						$this->value->SetAlign('center', 'bottom');
					} else {
						$this->value->SetAlign('center', 'top');
					}
				}
				$this->value->SetMargin(-5);
				$this->value->Stroke($img, $val, $x, $y);
			} elseif ($this->valuepos == 'center') {
				$y = ($pts[3] + $pts[1]) / 2;
				$this->value->SetAlign('center', 'center');
				$this->value->SetMargin(0);
				$this->value->Stroke($img, $val, $x, $y);
			} elseif ($this->valuepos == 'bottom' || $this->valuepos == 'min') {
				$y = $pts[1];
				if ($img->a === 90) {
					if ($val < 0) {
						$this->value->SetAlign('right', 'center');
					} else {
						$this->value->SetAlign('left', 'center');
					}
				}
				$this->value->SetMargin(3);
				$this->value->Stroke($img, $val, $x, $y);
			} else {
				Util\JpGraphError::RaiseL(2006, $this->valuepos);
				//'Unknown position for values on bars :'.$this->valuepos);
			}
			// Create the client side image map
			$rpts	  = $img->ArrRotate($pts);
			$csimcoord = round($rpts[0]) . ', ' . round($rpts[1]);
			for ($j = 1; $j < 4; ++$j) {
				$csimcoord .= ', ' . round($rpts[2 * $j]) . ', ' . round($rpts[2 * $j + 1]);
			}
			if (!empty($this->csimtargets[$i])) {
				$this->csimareas .= '<area shape="poly" coords="' . $csimcoord . '" ';
				$this->csimareas .= ' href="' . htmlentities($this->csimtargets[$i]) . '"';

				if (!empty($this->csimwintargets[$i])) {
					$this->csimareas .= ' target="' . $this->csimwintargets[$i] . '" ';
				}

				$sval = '';
				if (!empty($this->csimalts[$i])) {
					$sval = sprintf($this->csimalts[$i], $this->coords[0][$i]);
					$this->csimareas .= " title=\"${sval}\" alt=\"${sval}\" ";
				}
				$this->csimareas .= " />\n";
			}
		}

		return true;
	}
} // @class

/* EOF */
